package com.ben.multiple.elasticsearch.resource;

import com.ben.multiple.elasticsearch.entity.Person;
import com.ben.multiple.elasticsearch.mapper.PersonElasticsearchRepository;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.elasticsearch.client.elc.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.client.elc.NativeQuery;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.Criteria;
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
import org.springframework.data.elasticsearch.core.query.CriteriaQueryBuilder;
import org.springframework.data.elasticsearch.core.query.IndexQuery;
import org.springframework.data.elasticsearch.core.query.IndexQueryBuilder;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author ben
 */
@Slf4j
@RestController
@RequestMapping("person")
public class PersonRest {
    @Resource
    private PersonElasticsearchRepository personElasticsearchRepository;
    @Resource
    private ElasticsearchTemplate elasticsearchTemplate;

    @GetMapping("/one")
    public void insertOne() {
        Person one = PersonService.createOne();
        personElasticsearchRepository.save(one);
    }

    @GetMapping("/many")
    public Map<String, Object> insert(@RequestParam(name = "size") Integer size) {
        List<Person> list = PersonService.createBatch(size);
        int batchSize = 10000;
        int current = 0;
        int total = list.size();
        int i = 1;
        long it = System.currentTimeMillis();
        while (current < total) {
            List<Person> p = new ArrayList<>((current + batchSize < total) ? list.subList(current, current + batchSize) : list.subList(current, total));
            long st = System.currentTimeMillis();

//            personElasticsearchRepository.saveAll(p);

            batchInsert(p);

            log.info("batchInsert, i : {}, p size : {}, time : {}", i, p.size(), System.currentTimeMillis() - st);
            i++;
            current = current + batchSize;
        }
        long allTime = System.currentTimeMillis() - it;
        Map<String, Object> map = new HashMap<>();
        map.put("size", total);
        map.put("allTime", allTime);
        log.info("batchInsert, map : {}", map);
        return map;

    }

    private void batchInsert(List<Person> list) {
        List<IndexQuery> queryList = new ArrayList<>();
        for (Person obj : list) {
            IndexQueryBuilder builder = new IndexQueryBuilder()
                    .withId(String.valueOf(obj.getId()))
                    .withObject(obj)
                    .withOpType(IndexQuery.OpType.CREATE);
            queryList.add(builder.build());
        }
        elasticsearchTemplate.bulkIndex(queryList, Person.class);
    }

    @GetMapping("/delete/{id}")
    public void delete(@PathVariable Long id) {
        personElasticsearchRepository.deleteById(id);
    }


    @GetMapping
    public List<Person> list(@RequestParam(value = "name", required = false) String name) {
        SearchHits<Person> hits;
        if (StringUtils.hasText(name)) {
            Criteria criteria = Criteria.where("name").matches(name);
            CriteriaQuery query = new CriteriaQueryBuilder(criteria).build();
            hits = elasticsearchTemplate.search(query, Person.class);

        } else {
            hits = elasticsearchTemplate.search(NativeQuery.builder().build(), Person.class);
        }
        List<SearchHit<Person>> list = hits.getSearchHits();
        List<Person> r = new ArrayList<>();
        for (SearchHit<Person> hit : list) {
            r.add(hit.getContent());
        }
        return r;
    }


}
